﻿using MvCamCtrl.NET;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using thinger.DataConvertLib;
using thinger.VisionModels;

namespace thinger.VisitionDAL
{
   
    public class HikHelper
    {
        public HikHelper()
        {
            InitialCameraList();
        }


        //默认采集模式
        public MyCamera.MV_CAM_ACQUISITION_MODE DEFAULT_ACQUISITION_MODE = MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS;

        //默认触发模式
        public MyCamera.MV_CAM_TRIGGER_MODE DEFAULT_TRIGGER_MODE = MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON;

        //默认触发源
        public MyCamera.MV_CAM_TRIGGER_SOURCE DEFAULT_TRIGGER_SOURCE = MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_SOFTWARE;

        //MV_CC_DEVICE_INFO
        private List<MyCamera.MV_CC_DEVICE_INFO> m_pDeviceInfo = new List<MyCamera.MV_CC_DEVICE_INFO>();
        
        /// <summary>
        /// 相机全名列表
        /// </summary>
        public List<string> DeviceList { get; set; } = new List<string>();

        //DEVICE_INFO_LIST
        private MyCamera.MV_CC_DEVICE_INFO_LIST m_pDeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();

        /// <summary>
        /// 相机名称列表
        /// </summary>
        private List<string> CameraList { get; set; } = new List<string>();

        //MyCamera对象
        private MyCamera[] myCamera;

        /// <summary>
        /// 初始化设备列表
        /// </summary>
        public void InitialCameraList()
        {
            int nRet = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_pDeviceList);
            if (0 != nRet)
            {
                return;
            }
            m_pDeviceInfo = new List<MyCamera.MV_CC_DEVICE_INFO>();
            DeviceList = new List<string>();
            // ch:在窗体列表中显示设备名 | en:Display device name in the form list
            for (int i = 0; i < m_pDeviceList.nDeviceNum; i++)
            {
                MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_pDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
                if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
                {
                    MyCamera.MV_GIGE_DEVICE_INFO gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO));

                    if (gigeInfo.chUserDefinedName != "")
                    {
                        DeviceList.Add("GEV: " + gigeInfo.chUserDefinedName + " (" + gigeInfo.chSerialNumber + ")");
                    }
                    else
                    {
                        DeviceList.Add("GEV: " + gigeInfo.chManufacturerName + " " + gigeInfo.chModelName + " (" + gigeInfo.chSerialNumber + ")");
                    }
                }
                else if (device.nTLayerType == MyCamera.MV_USB_DEVICE)
                {
                    MyCamera.MV_USB3_DEVICE_INFO usbInfo = (MyCamera.MV_USB3_DEVICE_INFO)MyCamera.ByteToStruct(device.SpecialInfo.stUsb3VInfo, typeof(MyCamera.MV_USB3_DEVICE_INFO));
                    if (usbInfo.chUserDefinedName != "")
                    {
                        DeviceList.Add("U3V: " + usbInfo.chUserDefinedName + " (" + usbInfo.chSerialNumber + ")");
                    }
                    else
                    {
                        DeviceList.Add("U3V: " + usbInfo.chManufacturerName + " " + usbInfo.chModelName + " (" + usbInfo.chSerialNumber + ")");
                    }
                }
                m_pDeviceInfo.Add(device);
            }
        }

        //图像帧信息
        private MyCamera.MV_FRAME_OUT_INFO_EX[] m_stFrameInfo;

        //线程锁
        private static object[] m_BufForSaveImageLock;

        //保存图像Buffer
        private IntPtr[] m_pSaveImageBuf;

        //显示句柄
        public IntPtr[] m_hDisplayHandle;

        //保存图像的长度
        private uint[] m_nSaveImageBufSize = new uint[4] { 0, 0, 0, 0 };

        //曝光时间
        public float[] ExposureTime { get; set; }

        //增益
        public float[] Gain { get; set; }

        //帧率
        public float[] FrameRate { get; set; }

        //图像输出委托
        private MyCamera.cbOutputExdelegate cbImage;

        //图像显示事件
        public event Action<MyCamera.MV_DISPLAY_FRAME_INFO> ImageEvent;


        [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
        public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);


        #region 加载相机列表
        /// <summary>
        /// 加载相机列表
        /// </summary>
        /// <param name="cameraList">相机名称集合</param>
        public void LoadCameraList(List<string> cameraList)
        {
            this.CameraList = cameraList;

            myCamera = new MyCamera[cameraList.Count];

            m_stFrameInfo = new MyCamera.MV_FRAME_OUT_INFO_EX[cameraList.Count];

            m_BufForSaveImageLock = new object[cameraList.Count];

            m_pSaveImageBuf = new IntPtr[cameraList.Count];

            m_hDisplayHandle = new IntPtr[cameraList.Count];

            m_nSaveImageBufSize = new uint[cameraList.Count];

            ExposureTime = new float[cameraList.Count];

            Gain = new float[cameraList.Count];

            FrameRate = new float[cameraList.Count];

            cbImage = new MyCamera.cbOutputExdelegate(ImageCallBack);

            for (int i = 0; i < m_pSaveImageBuf.Length; i++)
            {
                m_pSaveImageBuf[i] = IntPtr.Zero;
            }
            for (int i = 0; i < m_hDisplayHandle.Length; i++)
            {
                m_hDisplayHandle[i] = IntPtr.Zero;
            }
            for (int i = 0; i < m_BufForSaveImageLock.Length; i++)
            {
                m_BufForSaveImageLock[i] = new object();
            }
            for (int i = 0; i < m_BufForSaveImageLock.Length; i++)
            {
                m_BufForSaveImageLock[i] = new object();
            }
            for (int i = 0; i < myCamera.Length; i++)
            {
                myCamera[i] = new MyCamera();
            }
        }

        #endregion

        #region 打开全部相机
        /// <summary>
        /// 打开全部相机
        /// </summary>
        /// <returns></returns>
        public List<OperateResult> OpenCamera()
        {
            var result = new List<OperateResult>();

            foreach (var item in this.CameraList)
            {
                var res = OpenCamera(item);

                if (res.IsSuccess)
                {
                    res.Message = $"相机【{item}】打开成功";
                }
                else
                {
                    res.Message = $"相机【{item}】打开失败：" + res.Message;
                }

                result.Add(res);
            }
            return result;
        }


        /// <summary>
        /// 根据相机名称，打开单个相机
        /// </summary>
        /// <param name="cameraName"></param>
        /// <returns></returns>
        private OperateResult OpenCamera(string cameraName)
        {
            if (DeviceList.Count == 0)
            {
                return OperateResult.CreateFailResult<List<string>>("无可用的相机设备，请先初始化");
            }

            int devIndex = DeviceList.Where(c => c.Contains(cameraName)).Select(c => DeviceList.IndexOf(c)).FirstOrDefault();

            if (devIndex == -1)
            {
                return OperateResult.CreateFailResult<List<string>>("相机名称不正确，请检查");
            }

            return OpenCamera(this.CameraList.IndexOf(cameraName), devIndex);
        }


        /// <summary>
        /// 打开单个相机
        /// </summary>
        /// <param name="cameraIndex">相机序号</param>
        /// <param name="devIndex">相机索引</param>
        /// <returns></returns>
        private OperateResult OpenCamera(int cameraIndex, int devIndex)
        {
            // ch:获取选择的设备信息 | en:Get selected device information
            MyCamera.MV_CC_DEVICE_INFO device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_pDeviceList.pDeviceInfo[devIndex], typeof(MyCamera.MV_CC_DEVICE_INFO));

            // ch:打开设备 | en:Open device

            if (myCamera[cameraIndex] == null)
            {
                myCamera[cameraIndex] = new MyCamera();
            }

            int nRet = myCamera[cameraIndex].MV_CC_CreateDevice_NET(ref device);
            if (MyCamera.MV_OK != nRet)
            {
                return OperateResult.CreateFailResult("创建相机设备失败：MV_CC_CreateDevice_NET");
            }

            nRet = myCamera[cameraIndex].MV_CC_OpenDevice_NET();
            if (MyCamera.MV_OK != nRet)
            {
                myCamera[cameraIndex].MV_CC_DestroyDevice_NET();
                return OperateResult.CreateFailResult("打开相机设备失败：MV_CC_OpenDevice_NET");
            }

            // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
            if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
            {
                int nPacketSize = myCamera[cameraIndex].MV_CC_GetOptimalPacketSize_NET();
                if (nPacketSize > 0)
                {
                    nRet = myCamera[cameraIndex].MV_CC_SetIntValue_NET("GevSCPSPacketSize", (uint)nPacketSize);
                    if (nRet != MyCamera.MV_OK)
                    {
                        return OperateResult.CreateFailResult("设置相机包大小失败：MV_CC_SetIntValue_NET");
                    }
                }
                else
                {
                    return OperateResult.CreateFailResult("获取相机包大小失败：MV_CC_GetOptimalPacketSize_NET");
                }
            }

            // ch:设置采集连续模式 | en:Set Continues Aquisition Mode

            nRet = SetAcquisitionMode(DEFAULT_ACQUISITION_MODE, cameraIndex);
            if (nRet != MyCamera.MV_OK)
            {
                return OperateResult.CreateFailResult("设置相机采集模式失败：MV_CC_SetEnumValue_NET_AcquisitionMode");
            }

            nRet = SetTriggerMode(DEFAULT_TRIGGER_MODE, cameraIndex);

            if (nRet != MyCamera.MV_OK)
            {
                return OperateResult.CreateFailResult("设置相机触发模式失败：MV_CC_SetEnumValue_NET_TriggerMode");
            }

            nRet = SetTriggerSource(DEFAULT_TRIGGER_SOURCE, cameraIndex);

            if (nRet != MyCamera.MV_OK)
            {
                return OperateResult.CreateFailResult("设置相机触发源失败：MV_CC_SetEnumValue_NET_TriggerSource");
            }

            nRet = myCamera[cameraIndex].MV_CC_RegisterImageCallBackEx_NET(cbImage, (IntPtr)cameraIndex);
            if (nRet == MyCamera.MV_OK)
            {
                GetCameraInfo(cameraIndex);
                return OperateResult.CreateSuccessResult();
            }
            else
            {
                return OperateResult.CreateFailResult("相机注册回调函数失败：MV_CC_RegisterImageCallBackEx_NET");
            }
        }
        #endregion

        #region 获取相机信息

        /// <summary>
        /// 获取相机信息
        /// </summary>
        /// <param name="cameraIndex"></param>
        private void GetCameraInfo(int cameraIndex)
        {
            MyCamera.MVCC_FLOATVALUE stParam = new MyCamera.MVCC_FLOATVALUE();
            int nRet = myCamera[cameraIndex].MV_CC_GetFloatValue_NET("ExposureTime", ref stParam);
            if (MyCamera.MV_OK == nRet)
            {
                ExposureTime[cameraIndex] = stParam.fCurValue;
            }

            nRet = myCamera[cameraIndex].MV_CC_GetFloatValue_NET("Gain", ref stParam);
            if (MyCamera.MV_OK == nRet)
            {
                Gain[cameraIndex] = stParam.fCurValue;
            }

            nRet = myCamera[cameraIndex].MV_CC_GetFloatValue_NET("ResultingFrameRate", ref stParam);
            if (MyCamera.MV_OK == nRet)
            {
                FrameRate[cameraIndex] = stParam.fCurValue;
            }
        }
        #endregion

        #region 设置相机模式
        /// <summary>
        /// 设置采集模式
        /// </summary>
        /// <param name="cameraIndex"></param>
        /// <param name="mode"></param>
        /// <returns></returns>
        public int SetAcquisitionMode(MyCamera.MV_CAM_ACQUISITION_MODE mode, int cameraIndex = 0)
        {
            return myCamera[cameraIndex].MV_CC_SetEnumValue_NET("AcquisitionMode", (uint)mode);
        }

        /// <summary>
        /// 设置触发模式
        /// </summary>
        /// <param name="cameraIndex"></param>
        /// <param name="mode"></param>
        /// <returns></returns>
        public int SetTriggerMode(MyCamera.MV_CAM_TRIGGER_MODE mode, int cameraIndex = 0)
        {
            return myCamera[cameraIndex].MV_CC_SetEnumValue_NET("TriggerMode", (uint)mode);
        }

        /// <summary>
        /// 设置触发源
        /// </summary>
        /// <param name="source"></param>
        /// <param name="cameraIndex"></param>
        /// <returns></returns>
        public int SetTriggerSource(MyCamera.MV_CAM_TRIGGER_SOURCE source, int cameraIndex = 0)
        {
            return myCamera[cameraIndex].MV_CC_SetEnumValue_NET("TriggerSource", (uint)source);
        }


        /// <summary>
        /// 软触发
        /// </summary>
        /// <param name="cameraIndex"></param>
        /// <returns></returns>
        public int TriggerExec(int cameraIndex = 0)
        {
            return myCamera[cameraIndex].MV_CC_SetCommandValue_NET("TriggerSoftware");
        }

        /// <summary>
        /// 设置曝光时间
        /// </summary>
        /// <param name="value"></param>
        /// <param name="cameraIndex"></param>
        /// <returns></returns>
        public int SetExposureTime(float value, int cameraIndex = 0)
        {
            int nRet = myCamera[cameraIndex].MV_CC_SetEnumValue_NET("ExposureAuto", 0);
            nRet = myCamera[cameraIndex].MV_CC_SetFloatValue_NET("ExposureTime", value);
            return nRet;
        }

        /// <summary>
        /// 设置增益
        /// </summary>
        /// <param name="value"></param>
        /// <param name="cameraIndex"></param>
        /// <returns></returns>
        public int SetGain(float value, int cameraIndex = 0)
        {
            int nRet = myCamera[cameraIndex].MV_CC_SetEnumValue_NET("GainAuto", 0);
            nRet = myCamera[cameraIndex].MV_CC_SetFloatValue_NET("Gain", value);
            return nRet;
        }

        /// <summary>
        /// 设置帧率
        /// </summary>
        /// <param name="value"></param>
        /// <param name="cameraIndex"></param>
        /// <returns></returns>
        public int SetFrameRate(float value, int cameraIndex = 0)
        {
            int nRet = myCamera[cameraIndex].MV_CC_SetFloatValue_NET("AcquisitionFrameRate", value);
            return nRet;
        }

        #endregion

        #region  关闭所有相机
        /// <summary>
        /// 关闭所有相机
        /// </summary>
        public void CloseCamera()
        {
            if (myCamera != null)
            {
                for (int i = 0; i < myCamera.Length; i++)
                {
                    // ch:关闭设备 | en:Close Device
                    myCamera[i]?.MV_CC_CloseDevice_NET();
                    myCamera[i]?.MV_CC_DestroyDevice_NET();
                }
            }
        }
        #endregion

        #region 开始采集
        /// <summary>
        /// 开始采集
        /// </summary>
        public void StartGrab()
        {
            for (int i = 0; i < this.CameraList.Count; i++)
            {
                m_stFrameInfo[i].nFrameLen = 0;//取流之前先清除帧长度
                m_stFrameInfo[i].enPixelType = MyCamera.MvGvspPixelType.PixelType_Gvsp_Undefined;
                myCamera[i].MV_CC_StartGrabbing_NET();
            }
        }

        #endregion

        #region 停止采集

        /// <summary>
        /// 停止采集
        /// </summary>
        public void StopGrab()
        {
            for (int i = 0; i < this.CameraList.Count; i++)
            {
                myCamera[i].MV_CC_StopGrabbing_NET();
            }
        }


        #endregion

        #region 保存图像

        /// <summary>
        /// 保存图像
        /// </summary>
        /// <param name="imageType"></param>
        /// <param name="path"></param>
        /// <param name="cameraIndex"></param>
        /// <returns></returns>
        public bool SaveImage(MyCamera.MV_SAVE_IAMGE_TYPE imageType, string path, int cameraIndex = 0)
        {
            MyCamera.MV_SAVE_IMG_TO_FILE_PARAM stSaveParam = new MyCamera.MV_SAVE_IMG_TO_FILE_PARAM();

            lock (m_BufForSaveImageLock[cameraIndex])
            {
                if (m_stFrameInfo[cameraIndex].nFrameLen == 0)
                {
                    return false;
                }
                stSaveParam.enImageType = imageType;
                stSaveParam.enPixelType = m_stFrameInfo[cameraIndex].enPixelType;
                stSaveParam.pData = m_pSaveImageBuf[cameraIndex];
                stSaveParam.nDataLen = m_stFrameInfo[cameraIndex].nFrameLen;
                stSaveParam.nHeight = m_stFrameInfo[cameraIndex].nHeight;
                stSaveParam.nWidth = m_stFrameInfo[cameraIndex].nWidth;
                stSaveParam.nQuality = imageType == MyCamera.MV_SAVE_IAMGE_TYPE.MV_Image_Png ? (uint)8 : imageType == MyCamera.MV_SAVE_IAMGE_TYPE.MV_Image_Jpeg ? (uint)80 : 0;
                stSaveParam.pImagePath = path;
                int nRet = myCamera[cameraIndex].MV_CC_SaveImageToFile_NET(ref stSaveParam);
                if (MyCamera.MV_OK != nRet)
                {
                    return false;
                }
            }
            return true;
        }



        #endregion

        #region 取流回调函数
        // 取流回调函数
        private void ImageCallBack(IntPtr pData, ref MyCamera.MV_FRAME_OUT_INFO_EX pFrameInfo, IntPtr pUser)
        {
            int nIndex = (int)pUser;

            // ch:抓取的帧数 | en:Aquired Frame Number

            lock (m_BufForSaveImageLock[nIndex])
            {
                if (m_pSaveImageBuf[nIndex] == IntPtr.Zero || pFrameInfo.nFrameLen > m_nSaveImageBufSize[nIndex])
                {
                    if (m_pSaveImageBuf[nIndex] != IntPtr.Zero)
                    {
                        Marshal.Release(m_pSaveImageBuf[nIndex]);
                        m_pSaveImageBuf[nIndex] = IntPtr.Zero;
                    }

                    m_pSaveImageBuf[nIndex] = Marshal.AllocHGlobal((Int32)pFrameInfo.nFrameLen);
                    if (m_pSaveImageBuf[nIndex] == IntPtr.Zero)
                    {
                        return;
                    }
                    m_nSaveImageBufSize[nIndex] = pFrameInfo.nFrameLen;
                }

                m_stFrameInfo[nIndex] = pFrameInfo;
                CopyMemory(m_pSaveImageBuf[nIndex], pData, pFrameInfo.nFrameLen);
            }

            MyCamera.MV_DISPLAY_FRAME_INFO stDisplayInfo = new MyCamera.MV_DISPLAY_FRAME_INFO();
            stDisplayInfo.hWnd = m_hDisplayHandle[nIndex];
            stDisplayInfo.pData = pData;
            stDisplayInfo.nDataLen = pFrameInfo.nFrameLen;
            stDisplayInfo.nWidth = pFrameInfo.nWidth;
            stDisplayInfo.nHeight = pFrameInfo.nHeight;
            stDisplayInfo.enPixelType = pFrameInfo.enPixelType;

            myCamera[nIndex].MV_CC_DisplayOneFrame_NET(ref stDisplayInfo);

            ImageEvent?.Invoke(stDisplayInfo);
        }
        #endregion



    }



}
